﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XDL.Framework.OpenSteer
{
    /// <summary>
    /// Defines a node in the path.
    /// </summary>
    public class PathNode :
        IPathNode
    {
        public static AStarPath ResolutionContext = null;
        int _costG = 0; // From start point to here
        PathNode m_parent = null;
        Vector2i _currentPoint;

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Create a new Node.
        /// </summary>
        /// <param name="parent"> The parent node. </param>
        /// <param name="currentPoint"> The current point. </param>
        /// ----------------------------------------------------------------------------------------
        public PathNode(PathNode parent, Vector2i position)
        {
            this._currentPoint = position;
            this.SetParent(parent);
        }

        #region Properties
        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Get or set the parent.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public PathNode Parent
        {
            get { return this.m_parent; }
            set { this.SetParent(value); }
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Get the cost.
        /// </summary>
        /// ----------------------------------------------------------------------------------------
        public int Cost
        {
            get { return this._costG; }
        }

        #endregion

        public class Comparer :
            IComparer<PathNode>
        {
            public int Compare(PathNode x, PathNode y)
            {
                if (x.F > y.F)
                    return 1;
                else if (x.F < y.F)
                    return -1;
                return 0;
            }
        }

        /// <summary>
        /// Set the parent.
        /// </summary>
        /// <param name="parent"> The parent to set. </param>
        private void SetParent(PathNode parent)
        {
            this.m_parent = parent;
            // Refresh the cost : the cost of the parent + the cost of the current point
            if (parent != null)
                this._costG =
                    m_parent.Cost + ResolutionContext.Map.GetCost(this._currentPoint);
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// The cost if you move to this.
        /// </summary>
        /// <returns> The futur cost. </returns>
        /// --------- -------------------------------------------------------------------------------
        public int CostWillBe()
        {
            return (this.m_parent != null ? m_parent.Cost + ResolutionContext.Map.GetCost(this._currentPoint) : 0);
        }

        /// ----------------------------------------------------------------------------------------
        /// <summary>
        /// Calculate the heuristic. (absolute x and y displacement).
        /// </summary>
        /// <returns> The heuristic. </returns>
        /// ----------------------------------------------------------------------------------------
        public int GetHeuristic()
        {
            return (Math.Abs(this._currentPoint.X - ResolutionContext.EndPoint.X) +
                Math.Abs(this._currentPoint.Y - ResolutionContext.EndPoint.Y));
        }

        public static int Directions = 8;
        static Vector2i[] g_quadrants = {
                                          new Vector2i(1,0),
                                          new Vector2i(0,1),
                                          new Vector2i(-1,0),
                                          new Vector2i(0,-1),
                                          new Vector2i(1,-1),
                                          new Vector2i(-1,-1),
                                          new Vector2i(1,1),
                                          new Vector2i(-1,1)
                                      };

        /// <summary>
        /// Get valid nodes around the current position.
        /// </summary>
        /// <param name="buffer">Buffer to store the set of valid <see cref="PathNode>"/></param>
        /// <returns>The number of valid points.</returns>
        public int GetNeighbors(PathNode[] buffer)
        {
            int index = 0;
            IMap map = ResolutionContext.Map;
            for (int i = 0; i < Directions; i++)
            {
                Vector2i testPosition = _currentPoint + g_quadrants[i];
                if (map.IsWalkable(testPosition))
                    buffer[index++] = new PathNode(this, testPosition);
            }
            return index;
        }

        /// <summary>
        /// Get valid nodes around the current position.
        /// </summary>
        /// <param name="buffer">Buffer to store the set of valid <see cref="PathNode>"/></param>
        /// <returns>The number of valid points.</returns>
        public int GetNeighbors(Vector2i[] buffer)
        {
            int index = 0;
            IMap map = ResolutionContext.Map;
            for (int i = 0; i < Directions; i++)
            {
                Vector2i testPosition = _currentPoint + g_quadrants[i];
                if (map.IsWalkable(testPosition))
                    buffer[index++] = testPosition;
            }
            return index;
        }

        #region IPathNode Members
        public int F
        {
            get { return this._costG + this.GetHeuristic(); }
        }

        public Vector2i Position
        {
            get { return _currentPoint; }
        }

        #endregion
    }
}
